package krow.dev.addetector.control;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import krow.dev.addetector.R;
import krow.dev.addetector.control.decompile.Extractor;
import krow.dev.addetector.model.ApplicationItem;
import krow.dev.addetector.view.dialog.ModuleScanDialog;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.os.AsyncTask;

public class ModuleSacnner extends AsyncTask<Void, Object, Boolean> {

	private static List<Module> sModuleList = new ArrayList<Module>();

	static {
		sModuleList.add(Module.TAd);
		sModuleList.add(Module.Adam);
		sModuleList.add(Module.AdMob);
		sModuleList.add(Module.AdMobService);
		sModuleList.add(Module.Dawin);
		sModuleList.add(Module.InMobi);
		sModuleList.add(Module.Adlib);
		sModuleList.add(Module.AppLift);
		sModuleList.add(Module.TapJoy);
		sModuleList.add(Module.MillennialMedia);
		sModuleList.add(Module.AppFlood);
		sModuleList.add(Module.MoPub);
		sModuleList.add(Module.ShallWeAd);
		sModuleList.add(Module.Mojise);
		sModuleList.add(Module.AirPush);
		sModuleList.add(Module.LeadBolt);
		sModuleList.add(Module.Moolah);
		sModuleList.add(Module.SendDroid);
		sModuleList.add(Module.AppLovin);
		sModuleList.add(Module.Appboy);
		sModuleList.add(Module.Amazon);
	}

	private Map<String, ApplicationItem> mApplicationItemMap;
	
	private Context mContext;
	private Extractor mExtract;
	private PackageManager mPackageManager;
	private OnCallbackListener mCallbackListener;
	private ModuleScanDialog mDialog;
	private boolean mIsCancelled;

	public ModuleSacnner(Activity activity) {
		mContext = activity;
		mExtract = new Extractor();
		mPackageManager = mContext.getPackageManager();
		mApplicationItemMap = new HashMap<String, ApplicationItem>();
	}

	public void setOnCallbackListener(OnCallbackListener callbackListener) {
		mCallbackListener = callbackListener;
	}

	public void stop() {
		if (mCallbackListener != null) {
			mCallbackListener.onCancel();
		}
		mIsCancelled = true;
		cancel(true);
	}

	@Override
	protected void onPreExecute() {
		if (mCallbackListener != null) {
			mCallbackListener.onStart();
		}

		mDialog = new ModuleScanDialog(mContext);
		mDialog.setTitle(mContext.getString(R.string.text_scan));
		mDialog.setCanceledOnTouchOutside(false);
		mDialog.setOnCancelListener(new OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				if (mCallbackListener != null) {
					mCallbackListener.onCancel();
				}
				mIsCancelled = true;
				cancel(true);
			}
		});
		mDialog.show();
	}

	@Override
	protected void onCancelled() {
		super.onCancelled();
		if (mDialog != null) {
			mDialog.dismiss();
			mDialog = null;
		}
	}

	@Override
	protected Boolean doInBackground(Void... params) {

		boolean isSucceed = true;
		List<PackageInfo> packageInfoList = mPackageManager.getInstalledPackages(0);

		int size = packageInfoList.size();
		for (int i = 0; i < size; i++) {
			//FIXME isCancelled()
			if (mIsCancelled) { 
				isSucceed = false;
				break;
			}

			try {
				PackageInfo packageInfo = mPackageManager.getPackageInfo(packageInfoList.get(i).packageName,
						PackageManager.GET_ACTIVITIES
						|PackageManager.GET_SERVICES
						|PackageManager.GET_RECEIVERS);
				publishProgress(packageInfo.packageName, i + 1, size);
				
				int flag = packageInfo.applicationInfo.flags;
				if ((flag & ApplicationInfo.FLAG_SYSTEM) != 0 || (flag & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
					continue;
				}

				if (PackageManager.PERMISSION_GRANTED == mPackageManager.checkPermission(Manifest.permission.INTERNET, packageInfo.packageName)) {
					parsePackage(packageInfo);
				}
			} catch (Exception e) {

			}
		}
		
		try {
			Thread.sleep(500);
		} catch (Exception e) {

		}
		
		return isSucceed;
	}

	@Override
	protected void onProgressUpdate(Object... items) {
		try {
			String name = (String) items[0];
			int index = (Integer) items[1];
			int size = (Integer) items[2];

			if (mCallbackListener != null) {
				mCallbackListener.onProgress(name, index, size);
			}

			if (mDialog != null) {
				mDialog.setProgress(index, size);
			}
		} catch (Exception e) {

		}
	}

	@Override
	protected void onPostExecute(Boolean result) {

		if (result && mCallbackListener != null) {
			mCallbackListener.onCompletion(new ArrayList<ApplicationItem>(mApplicationItemMap.values()));
		}

		if (mDialog != null) {
			mDialog.dismiss();
			mDialog = null;
		}
	}

	private void parsePackage(PackageInfo packageInfo) throws Exception {

		PackageItemInfo[] activityInfoArray = packageInfo.activities;
		PackageItemInfo[] serviceInfoArray = packageInfo.services;
		PackageItemInfo[] receiverInfoArray = packageInfo.receivers;
		
		if(activityInfoArray.length > 0){
			checkModle(packageInfo, activityInfoArray);
		}
		if(serviceInfoArray.length > 0){
			checkModle(packageInfo, serviceInfoArray);
		}
		if(receiverInfoArray.length > 0){
			checkModle(packageInfo, receiverInfoArray);
		}
	}

	private void checkModle(PackageInfo packageInfo, PackageItemInfo[] pakcageItemInfoArray){
		for (PackageItemInfo packageItemInfo : pakcageItemInfoArray) {
			for (int i = 0; i < sModuleList.size(); i++) {
				Module module = sModuleList.get(i);
				if (packageItemInfo.name.indexOf(module.getName()) >= 0) {
					ApplicationItem applicationItem = mApplicationItemMap.get(packageInfo.packageName);
					if(applicationItem == null){
						applicationItem = new ApplicationItem(packageInfo);
						mApplicationItemMap.put(packageInfo.packageName, applicationItem);
					}
					applicationItem.addModule(module);
				}
			}
		}
	}
	
	//XXX AndroidManifest Decompile
	@SuppressWarnings("unused")
	@Deprecated
	private void parse(PackageInfo packageInfo) throws Exception {
		String path = packageInfo.applicationInfo.sourceDir;
		if (path == null) {
			return;
		}

		File file = new File(path);
		if (!file.exists()) {
			return;
		}

		checkRule(packageInfo, mExtract.getManifest(path));
	}

	@Deprecated
	private void checkRule(PackageInfo packageInfo, String xml) {

		String[] stringArray = xml.split(System.getProperty("line.separator"));
		for (int i = 0; i < stringArray.length; i++) {
			String value = stringArray[i];
			for (int j = 0; j < sModuleList.size(); j++) {
				Module module = sModuleList.get(j);
				if (value.indexOf(module.getName()) > 0) {
					ApplicationItem applicationItem = mApplicationItemMap.get(packageInfo.packageName);
					if(applicationItem == null){
						applicationItem = new ApplicationItem(packageInfo);
						mApplicationItemMap.put(packageInfo.packageName, applicationItem);
					}
					applicationItem.addModule(module);
				}
			}
		}
	}

	public interface OnCallbackListener {

		public abstract void onStart();

		public abstract void onProgress(String name, int index, int size);

		public abstract void onCancel();

		public abstract void onCompletion(List<ApplicationItem> applicationItemList);
	}
}
